
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

public class Ip {

    private int[] prefStart = new int[256];
    private int[] prefEnd = new int[256];
    private byte[] data;
    private static Ip instance = null;

    private Ip(String fileName) {
        Path path = Paths.get(fileName);

        try {
            data = Files.readAllBytes(path);
        } catch (IOException e) {
            e.printStackTrace();
        }

        for (int k = 0; k < 256; k++) {
            int i = k * 8 + 4;
            prefStart[k] = (int) unpackInt4byte(data[i], data[i + 1], data[i + 2], data[i + 3]);
            prefEnd[k] = (int) unpackInt4byte(data[i + 4], data[i + 5], data[i + 6], data[i + 7]);
        }
    }

    public static synchronized Ip getInstance(String fileName) {
        if (instance == null)
            instance = new Ip(fileName);
        return instance;
    }

    public String get(String ip) {

        String[] ips = ip.split("\\.");
        int pref = Integer.valueOf(ips[0]);
        long val = ipToLong(ip);
        int low = prefStart[pref], high = prefEnd[pref];
        long cur = low == high ? low : search(low, high, val);
        return getAddr((int) cur);

    }

    private String getAddr(int cur){
        int p = 2052 + (cur * 9);
        int offset = (int)unpackInt4byte(data[4 + p], data[5 + p], data[6 + p],data[7+p]);
        int length = data[8 + p] & 0xff;
        return new String(Arrays.copyOfRange(data,offset,offset + length));
    }

    private int search(int low, int high, long k) {
        int M = 0;
        while (low <= high) {
            int mid = (low + high) / 2;

            int p = 2052 + (mid * 9);
            long endipNum = unpackInt4byte(data[p], data[1 + p], data[2 + p], data[3 + p]);

            if (endipNum >= k) {
                M = mid;
                if (mid == 0) {
                    break;
                }
                high = mid - 1;
            } else
                low = mid + 1;
        }
        return M;
    }

    private long unpackInt4byte(byte a, byte b, byte c, byte d) {
        return (a & 0xFFL) | ((b << 8) & 0xFF00L) | ((c << 16) & 0xFF0000L) | ((d << 24) & 0xFF000000L);

    }

    private long ipToLong(String ip) {
        long result = 0;
        String[] d = ip.split("\\.");
        for (String b : d) {
            result <<= 8;
            result |= Long.parseLong(b) & 0xff;
        }
        return result;
    }


}

